package com.fizzkai.p2p.bussness.service.impl;

import com.fizzkai.p2p.bussness.domain.BidRequest;
import com.fizzkai.p2p.bussness.domain.PaymentSchedule;
import com.fizzkai.p2p.bussness.domain.PaymentScheduleDetail;
import com.fizzkai.p2p.bussness.domain.SystemAccount;
import com.fizzkai.p2p.bussness.mapper.PaymentScheduleMapper;
import com.fizzkai.p2p.bussness.service.IAccountFlowService;
import com.fizzkai.p2p.bussness.service.IBidRequestService;
import com.fizzkai.p2p.bussness.service.IBidService;
import com.fizzkai.p2p.bussness.service.IPaymentScheduleDetailService;
import com.fizzkai.p2p.bussness.service.IPaymentScheduleService;
import com.fizzkai.p2p.bussness.service.ISystemAccountFlowService;
import com.fizzkai.p2p.bussness.service.ISystemAccountService;
import com.fizzkai.p2p.bussness.util.CalculatetUtil;
import com.fizzkai.p2p.core.domain.Account;
import com.fizzkai.p2p.core.exception.DisplayableException;
import com.fizzkai.p2p.core.query.QueryObject;
import com.fizzkai.p2p.core.service.IAccountService;
import com.fizzkai.p2p.core.util.Constants;
import com.fizzkai.p2p.core.util.UserContext;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Transactional
public class PaymentScheduleServiceImpl implements IPaymentScheduleService {

    @Autowired
    private PaymentScheduleMapper paymentScheduleMapper;
    @Autowired
    private IPaymentScheduleDetailService paymentScheduleDetailService;
    @Autowired
    private IAccountService accountService;
    @Autowired
    private IAccountFlowService accountFlowService;
    @Autowired
    private ISystemAccountService systemAccountService;
    @Autowired
    private ISystemAccountFlowService systemAccountFlowService;
    @Autowired
    private IBidService bidService;
    @Autowired
    private IBidRequestService bidRequestService;

    @Override
    public int save(PaymentSchedule entity) {
        return paymentScheduleMapper.insert(entity);
    }

    @Override
    public int update(PaymentSchedule entity) {
        return paymentScheduleMapper.updateByPrimaryKey(entity);
    }

    @Override
    public PaymentSchedule get(Long id) {
        return paymentScheduleMapper.selectByPrimaryKey(id);
    }

    @Override
    public PageInfo<PaymentSchedule> query(QueryObject qo) {
        PageHelper.startPage(qo.getPageNum(), qo.getPageSize());
        List<PaymentSchedule> list = paymentScheduleMapper.queryList(qo);
        return new PageInfo<>(list);
    }

    @Override
    public void returnMoney(Long id) {
        Long loginId = UserContext.getCurrentLoginInfo().getId();
        PaymentSchedule ps = this.get(id);
        Account borrowAccount = accountService.get(loginId);
        // 1. 拦截非法

        if (ps == null  // 还款单 不存在
            || !ps.getBorrowUser().getId().equals(loginId)  // 借款对象 不是 还款对象
            || ps.getState() == Constants.PAYMENT_STATE_DONE    // 还款单状态不对
            ) {
            throw new DisplayableException("非法操作");
        }
        // 没有足够的 钱
        if (borrowAccount.getUsableAmount().compareTo(ps.getTotalAmount()) < 0) {
            throw new DisplayableException("钱不够");
        }
        // 2. 借款人
        // 2.1  borrowAccount 减少 待还 本金，可用 余额
        borrowAccount.setUsableAmount(borrowAccount.getUsableAmount().subtract(ps.getTotalAmount()));
        borrowAccount.setUnReturnAmount(borrowAccount.getUnReturnAmount().subtract(ps.getTotalAmount()));
        // 2.2  剩余授信 额度 增加
        borrowAccount.setRemainBorrowLimit(borrowAccount.getRemainBorrowLimit().add(ps.getPrincipal()));
        //      更新 account
        accountService.update(borrowAccount);
        //      记录流水, 还款 成功流水
        accountFlowService.createReturnMoneyFlow(borrowAccount, ps.getTotalAmount());

        // 3. 投标人
        // 定义map 接收 account
        Map<Long, Account> bidsAccount = new HashMap<>();
        // 4.0 定义系统账户 变量
        SystemAccount systemAccount = systemAccountService.getCurrent();
        // 抽取 变量
        Account bidAccount;

        // 3.1 bidAccount 添加 money 减少 待收本金，待收 利息, 添加 可用 余额
        for (PaymentScheduleDetail psd : ps.getDetails()) {
            Long investorId = psd.getInvestorId();
            bidAccount = bidsAccount.get(investorId);
            if (bidAccount == null) {
                bidAccount = accountService.get(investorId);
                bidsAccount.put(investorId, bidAccount);
            }
            bidAccount.setUsableAmount(bidAccount.getUsableAmount().add(psd.getTotalAmount()));
            bidAccount.setUnReceivePrincipal(bidAccount.getUnReceivePrincipal().subtract(psd.getPrincipal()));
            bidAccount.setUnReceiveInterest(bidAccount.getUnReceiveInterest().subtract(psd.getInterest()));
            // 记录 回款成功 流水
            accountFlowService.createGainReturnMoneyFlow(bidAccount, psd.getTotalAmount());
            
            // 3.2 系统账户 收取 利息的 管理费
            BigDecimal interestManagerCharge = CalculatetUtil.calInterestManagerCharge(psd.getInterest());
            bidAccount.setUsableAmount(bidAccount.getUsableAmount().subtract(interestManagerCharge));
            //      记录流水
            accountFlowService.createPayInterestManagerChargeFlow(bidAccount, interestManagerCharge);

            // 4. 系统账户
            // 4.1 添加 money
            systemAccount.setUsableAmount(systemAccount.getUsableAmount().add(interestManagerCharge));
            //      记录流水
            systemAccountFlowService.createGainInterestManagerChargeFlow(systemAccount, interestManagerCharge);

        }

        // 3.3 更新 投标对象的 account
        bidsAccount.values().forEach(accountService::update);
        // 4.2 更新 系统账户
        systemAccountService.update(systemAccount);

        // 5. 还款对象
        // 5.1  包括子对象 全部状态 // 添加 还款日期
        Date now = new Date();
        ps.setPayDate(now);
        ps.setState(Constants.PAYMENT_STATE_DONE);
        this.update(ps);
        //      子对象 一条 SQL 搞定
        paymentScheduleDetailService.updatePayDate(ps.getId(), now);


        // 6. 判断 全部还完
        // 6.1 获取 一个 bidRequest 的全部 ps
        List<PaymentSchedule> psList = paymentScheduleMapper.selectByBidRequestId(ps.getBidRequestId());
        boolean isAllPay = psList.stream().allMatch(item -> item.getState() == Constants.PAYMENT_STATE_DONE);
        if (!isAllPay) {
            // 有些没还
            return;
        }
        // 全部还了
        // 6.2  借款对象 标对象，修改 状态 ，变成 已经还清
        BidRequest bidRequest = bidRequestService.get(ps.getBidRequestId());
        bidRequest.setBidRequestState(Constants.BIDREQUEST_STATE_COMPLETE_PAY_BACK);
        bidRequestService.update(bidRequest);
        // 6.3 投标对象 状态
        bidService.updateState(bidRequest.getId(), Constants.BIDREQUEST_STATE_COMPLETE_PAY_BACK);
    }
}
